#include "misc.h"
#include <stdio.h>
#include "md5.h"
#include "sha1.h"
#include <string.h>
#include <ctype.h>
#include "global_declarations.h"

int binary2hexa(const unsigned char *bufIn, int lengthIn,
				char *outStr, int outMaxLen) {
	int i;
	for (i = 0; i < lengthIn; i++) {
		outStr[2*i] = "0123456789abcdef"[(bufIn[i]>>4)];
		outStr[2*i+1] = "0123456789abcdef"[(bufIn[i] & 0x0f)];
	}
	return 0;
}


int MD5BasicHash ( const unsigned char *in,int len, unsigned char *outBuf) {
	md5_state_t ctx;
	md5_init(&ctx);
	md5_append(&ctx, in, len);
	md5_finish(&ctx, outBuf);
	return MD5_OUTPUT_LENGTH_IN_BYTES;
}

int SHA1BasicHash ( const unsigned char *in,int len, unsigned char *outBuf) {
	int i;
	SHA1Context ctx;
	SHA1Reset(&ctx);
	SHA1Input(&ctx, in, len);
	SHA1Result(&ctx);
	for (i = 0; i<5; i++) {
		outBuf[i*4] = ((unsigned char*)&ctx.Message_Digest[i])[3];
		outBuf[i*4+1] = ((unsigned char*)&ctx.Message_Digest[i])[2];
		outBuf[i*4+2] = ((unsigned char*)&ctx.Message_Digest[i])[1];
		outBuf[i*4+3] = ((unsigned char*)&ctx.Message_Digest[i])[0];
	}
	return SHA1_OUTPUT_LENGTH_IN_BYTES;
}

int cryptHash ( BasicHashFunctionPtr cryptHashPtr, const unsigned char *passwd, unsigned char *outBuf ) {
	return cryptHashPtr(passwd, strlen((char *)passwd), outBuf);
}

int getValueOfHexDigit(char c) {
	if (isdigit((int)c)) {
		return c - '0';
	}
	switch(c) {
	case 'a':
		return 10;
	case 'b':
		return 11;
	case 'c':
		return 12;
	case 'd':
		return 13;
	case 'e':
		return 14;
	case 'f':
		return 15;
	default:
		return -1;
	}
}

int hexa2binary(const char *strIn, unsigned char *outBuf, int outMaxLen) {
	int i;
	for (i = 0; i < outMaxLen; i++) {
		outBuf[i] = (getValueOfHexDigit(strIn[2*i]) << 4) | getValueOfHexDigit(strIn[2*i + 1]);
	}
	return 0;
}

#define MAX_SEED_USAGE 24

int64 pseudo_random_function(const unsigned char *x,int inputLength,int64 seed)
/* You are allowed to change it or keep as is. Up to you*/
{
    int64 md5res[MD5_OUTPUT_LENGTH_IN_BYTES/sizeof(int64)];
    unsigned char buffer4hashing[MAX_SEED_USAGE+sizeof(int64)];

    if(inputLength>MAX_SEED_USAGE) inputLength = MAX_SEED_USAGE;
        /*for efficiency purpose*/
    memcpy(buffer4hashing,x,inputLength);/*copy seed itself*/
    memcpy(buffer4hashing+inputLength,&seed,sizeof(int64));
        /*concatenate step to the seed*/
    MD5BasicHash( buffer4hashing, inputLength+sizeof(int64) , (unsigned char *)md5res );
       /*main step, hash both seed and index as fusion process*/
    /*now just harvest 63 bit out of 128*/
    return ((md5res[0])&0x7fffffffffffffffL);
}

int64 pseudo_random_generator_proj(unsigned char *seed,
                                             int seedLength,
                                           int64 step)

/* You are allowed to change it or keep as is. Up to you*/
/* This function is called less frequently. Thus can be slightly slower*/
/* It is modified a little bit, to add additional and different bit-mix*/
{
    int64 md5res[MD5_OUTPUT_LENGTH_IN_BYTES/sizeof(int64)];
    int64 buffer4hashing[ 2 + MAX_SEED_USAGE/sizeof(int64)];

    if(seedLength>MAX_SEED_USAGE) seedLength = MAX_SEED_USAGE;
    /*initiate bits to some non-trivial but deterministic values*/
    /*thus bits we will not touch will have a certain value and not garbadge*/
    memset ( buffer4hashing , 0x3b , (2+MAX_SEED_USAGE/sizeof(int64))*sizeof(int64) );
    /*add some junk, so it is effective even if "seed" is a boring string*/
    buffer4hashing[0] = 0xbadf00d ;
    /*insert deterministic 1:1 function of step itself*/
    buffer4hashing[1] = step ^ (int64)(0x39d46826);
    memcpy( buffer4hashing+2 , seed , seedLength ) ;

    MD5BasicHash( (unsigned char *)buffer4hashing, (2+MAX_SEED_USAGE/sizeof(int64))*sizeof(int64) , (unsigned char *)md5res );
       /*main step, hash both seed and index as fusion process*/
    /*now just harvest 63 bit out of 128*/
    return ((md5res[1])&0x7fffffffffffffffL);

}

